static_cell 0.0.0

Cell that allows putting in a value once.
Documentation
#![no_std]

use core::cell::UnsafeCell;
use core::mem::MaybeUninit;

use atomic_polyfill::{AtomicBool, Ordering};

/// Type with static lifetime that may be written to once at runtime.
///
/// This may be used to initialize static objects at runtime, typically in the init routine.
/// This is useful for objects such as Embassy's RTC, which cannot be initialized in a const
/// context.
///
/// Note: IF a global mutable variable is desired, use a CriticalSectionMutex or ThreadModeMutex instead.
///
/// ```
/// use put_cell::PutCell;
///
/// static SOME_INT: PutCell<u32> = PutCell::new();
///
/// // put returns a mutable reference to the object stored in the PutCell, which may then be passed
/// // around.
/// let x: &'static mut u32 = SOME_INT.put(42);
/// assert_eq!(*x, 42);
/// ```
pub struct StaticCell<T> {
    used: AtomicBool,
    val: UnsafeCell<MaybeUninit<T>>,
}

unsafe impl<T> Send for StaticCell<T> {}
unsafe impl<T> Sync for StaticCell<T> {}

impl<T> StaticCell<T> {
    /// Create a new `PutCell`.
    #[inline(always)]
    pub const fn new() -> Self {
        Self {
            used: AtomicBool::new(false),
            val: UnsafeCell::new(MaybeUninit::uninit()),
        }
    }

    /// Put a value in the `PutCell`, returning a mutable reference to it.
    ///
    /// Using this method, the compiler usually constructs `val` in the stack and then moves
    /// it into the `PutCell`. If `T` is big, this is likely to cause stack overflows.
    /// Considering using [`PutCell::put_with`] instead, which will construct it in-place inside the `PutCell`.
    ///
    /// # Panics
    ///
    /// Panics if this `PutCell` already has a value stored in it.
    #[inline]
    #[allow(clippy::mut_from_ref)]
    pub fn put(&self, val: T) -> &mut T {
        self.put_with(|| val)
    }

    /// Put the closure's return value in the `PutCell`, returning a mutable reference to it.
    ///
    /// The advantage over [`PutCell::put`] is that this method allows the closure to construct
    /// the `T` value in-place directly inside the `PutCell`, saving stack space.
    ///
    /// # Panics
    ///
    /// Panics if this `PutCell` already has a value stored in it.
    #[inline]
    #[allow(clippy::mut_from_ref)]
    pub fn put_with(&self, val: impl FnOnce() -> T) -> &mut T {
        if self
            .used
            .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
            .is_err()
        {
            panic!("PutCell::put() called multiple times");
        }

        let p: &mut MaybeUninit<T> = unsafe { &mut *self.val.get() };
        p.write(val())
    }
}

impl<T> Drop for StaticCell<T> {
    fn drop(&mut self) {
        if self.used {
            // safety: the contents are initialized if `used` is set.
            unsafe { self.val.get().assume_init_drop() }
        }
    }
}